# -*- coding: UTF-8 -*-
from dtrader.portfolio import AbstractPortfolio
from dtrader.portfolio.position import Position

class Portfolio(AbstractPortfolio):
    """
    组合管理
    """

    def __init__(self,price_handler,cash,*positions):
        """
        组合
        :param price_handler:
        :param cash:
        :param Position *positions:
        """
        #计算现金，总资产
        self.price_handler = price_handler
        self.init_cash = cash
        self.equity = cash
        self.cur_cash = cash
        self.positions = {}
        self.cash = cash
        self.realised_pnl = 0
        self.closed_positions = []

        for position in positions:
            self.positions[position.ticker] = position

    def _update_portfolio(self):
        """
        更新组合
        """
        self.unrealised_pnl = 0
        #仓位关闭后才算实现的利润
        self.equity = self.realised_pnl
        self.equity += self.init_cash

        for ticker in self.positions:
            pt = self.positions[ticker]
            if self.price_handler.istick():
                bid, ask = self.price_handler.get_best_bid_ask(ticker)
            else:
                close_price = self.price_handler.get_last_close(ticker)
                bid = close_price
                ask = close_price
            pt.update_market_value(bid, ask)
            self.unrealised_pnl += pt.unrealised_pnl
            self.equity += (
                pt.market_value - pt.avg_price * pt.quantity + pt.realised_pnl
            )

    def _add_position(
            self, ticker,
            quantity, price, commission
    ):
        """
        添加一个新仓位
        """
        if ticker not in self.positions:
            if self.price_handler.istick():
                bid, ask = self.price_handler.get_best_bid_ask(ticker)
            else:
                close_price = self.price_handler.get_last_close(ticker)
                bid = close_price
                ask = close_price
            position = Position(ticker, quantity,
                price, bid, ask,commission
            )
            self.positions[ticker] = position
            self._update_portfolio()

    def _modify_position(
            self, action, ticker,
            quantity, price, commission
    ):
        """
        更新一个仓位
        """
        if ticker in self.positions:
            self.positions[ticker].transact_shares(
                action, quantity, price, commission
            )
            if self.price_handler.istick():
                bid, ask = self.price_handler.get_best_bid_ask(ticker)
            else:
                close_price = self.price_handler.get_last_close(ticker)
                bid = close_price
                ask = close_price
            self.positions[ticker].update_market_value(bid, ask)

            if self.positions[ticker].quantity == 0:
                closed = self.positions.pop(ticker)
                self.realised_pnl += closed.realised_pnl
                self.closed_positions.append(closed)

            self._update_portfolio()

    def transact_position(
            self, action, ticker,
            quantity, price, commission
    ):
        """
        Handles any new position or modification to
        a current position, by calling the respective
        _add_position and _modify_position methods.
        Hence, this single method will be called by the
        PortfolioHandler to update the Portfolio itself.
        """

        if action == "BOT":
            self.cur_cash -= ((quantity * price) + commission)
        elif action == "SLD":
            self.cur_cash += ((quantity * price) - commission)

        if ticker not in self.positions:
            self._add_position(
                ticker, quantity,
                price, commission
            )
        else:
            self._modify_position(
                action, ticker, quantity,
                price, commission
            )


    def get_positioin_percent(self):

        """
        返回仓位
        :return:
        """
        self.update_portfolio()
        total_market_value = 0
        for ticker in self.positions:
            pt = self.positions[ticker]
            total_market_value += pt.market_value

        return (total_market_value // self.equity) * 100


    def _convert_fill_to_portfolio_update(self, fill_event):

        """
        当接受到一个FillEvent，PortfolioHandler把交易成功的数据存入Portfolio投资组合
        对象中，确保交易账户和本地投资组合同步

        """
        action = fill_event.action
        ticker = fill_event.ticker
        quantity = fill_event.quantity
        price = fill_event.price
        commission = fill_event.commission
        # 通过成交事件创建或修改头寸
        self.transact_position(
            action, ticker, quantity,
            price, commission
        )


    def on_fill(self, fill_event):
        """
        这个方法会被回测模块或交易模块调用来根据信号事件来更新更新投资组合，创建
        或修改一个投资组合里面的头寸

        在回测模块这个事件会被模拟产生，在实盘交易中，这个事件会被交易接口直接产生
        """
        self._convert_fill_to_portfolio_update(fill_event)

    def update_portfolio_value(self):
        """
        当市场行情价格变化时候自动更新投资组合里面的参数
        """
        self._update_portfolio()




